#include <trap.h>

#include <um.h>
#include <assert.h>
#include <stdio.h>
#include <pmm.h> 				/* tlb_flush */
#include <vmm.h>				/* do_pgfault */

void segv_handler (int, struct um_pt_regs*);
void io_handler (int, struct um_pt_regs*);

void (*sig_handlers[NSIG])(int sig, struct um_pt_regs* regs) = {
	[SIGSEGV]      = segv_handler,
	[SIGIO]        = io_handler
};


/**
 * make a copy of the signal context
 * @param regs container for the context
 * @param context given by the host
 */
static void
copy_sc (struct um_pt_regs* regs, struct sigcontext* from) 
{
	regs->regs.eax = from->eax;
	regs->regs.ebx = from->ebx;
	regs->regs.ecx = from->ecx;
	regs->regs.edx = from->edx;
	regs->regs.eip = from->eip;
	
	regs->faultinfo.cr2 = from->cr2;
	regs->faultinfo.error_code = from->err;
	regs->faultinfo.trap_no = from->trapno;
}


/**
 * call the corresponding signal handler according to the sig number
 * @param sig signal serial number
 * @param sc context of the signal
 */
void
handle_signal (int sig, struct sigcontext *sc)
{
	//cprintf ("signal captured: %d (%s)\n", sig, sig_names[sig]);
	struct um_pt_regs r;
	if (sig == SIGSEGV)
		copy_sc (&r, sc);
	(*sig_handlers[sig]) (sig, &r);
}


/**
 * The entry of all signal handlers
 * @param sig signal serial number
 */
void
hard_handler (int sig)
{
	handle_signal (sig, (struct sigcontext*)(&sig+1));
}


/**
 * Register signal handler
 * @param sig signal serial number
 * @param handler the signal handler specified
 * @param flags sigaction-related flags
 */
void
set_handler (int sig, void(*handler)(int), int flags, ...)
{
	struct sigaction action;
	action.sa_handler = hard_handler;
	sigemptyset (&action.sa_mask);
	action.sa_flags = flags;
	action.sa_restorer = NULL;
	if (syscall3 (__NR_sigaction, sig, (long)&action, 0) < 0)
		panic ("sigaction failed\n");
}


/**
 * Check whether traps work as expected
 */
void
check_trap (void)
{
	/* SIGSEGV */
	*(int*)(0xB0000000) = 15;
	
	cprintf ("check_trap() succeeded.\n");
}


/**
 * Initialize basic signal handlers
 */
void
trap_init (void)
{
	set_handler (SIGSEGV, NULL, 0);

	/* raise signals such as SIGIO or SIGSEGV is fatal
	 * checks are just for debug
	 */
	/* check_trap (); */
}


/**************************************************
 * Specific signal handlers
 **************************************************/

/**
 * an error has occured.
 */
void
bad_segv (void ) 
{
	panic ("Segmentation fault\n");
}

/**
 * mark a page as accessed or dirty and flush tlb
 * @param mm mm_struct from which to get pgdir
 * @param is_write whether writing is performed
 * @param addr the logical address to be accessed
 */
void
do_pgmark (struct mm_struct* mm, uint32_t is_write, uintptr_t addr)
{
	struct vma_struct *vma = find_vma(mm, addr);
	if (vma == NULL || vma->vm_start > addr) {
        goto failed;
    }

	pte_t* pte = get_pte (mm->pgdir, addr, 0);
	if (pte == 0) {
		goto failed;
	}
	if (is_write && Get_PTE_W(pte) == 0) {
		goto failed;
	}

	Set_PTE_A (pte);
	if (is_write) {
		Set_PTE_D (pte);
	}

	tlb_flush (mm->pgdir, addr);

	return;

failed:
	bad_segv ();
}

/**
 * SIGSEGV handler
 *     give an error message first
 * @param sig signal serial number (of no use?)
 * @param regs register contexts of the fault
 */
void
segv_handler (int sig, struct um_pt_regs *regs) 
{
	cprintf ("[SIGSEGV] visiting 0x%x, eip = 0x%x, error = 0x%x\n",
			 regs->faultinfo.cr2, regs->regs.eip, regs->faultinfo.error_code);

	extern struct mm_struct *check_mm_struct;
	if (check_mm_struct == NULL)
		bad_segv ();

	pte_t* pte = get_pte (check_mm_struct->pgdir, regs->faultinfo.cr2, 0);
	if (pte == 0 || Get_PTE_P(pte) == 0) { /* Page not present. Normal page fault */
		do_pgfault (check_mm_struct, regs->faultinfo.error_code, regs->faultinfo.cr2);
	} else {					/* a clean page to be accessed */
		do_pgmark (check_mm_struct, (regs->faultinfo.error_code & FAULT_W ? 1 : 0), regs->faultinfo.cr2);
	}
	return;
}


/**
 * SIGIO handler
 *     silently ignored
 * @param sig signal serial number
 * @param regs register contexts of the fault
 */
void
io_handler (int sig, struct um_pt_regs *regs)
{
}
